"""State machine for managing BLE/WiFi service transitions."""

import asyncio
import logging
from enum import Enum
from typing import Optional, Dict, Any, Callable
from datetime import datetime, timedelta

from .monitor import NetworkMonitor, ConnectivityStatus
from .ble_manager import BLEServiceManager

logger = logging.getLogger(__name__)


class NetworkState(Enum):
    """Network service state enumeration."""
    BLE_ACTIVE = "ble_active"  # BLE service running, waiting for config
    WIFI_CONNECTING = "wifi_connecting"  # WiFi connection in progress
    WIFI_VERIFYING = "wifi_verifying"  # Verifying network connectivity
    WIFI_CONNECTED = "wifi_connected"  # WiFi connected, BLE disabled
    WIFI_DISCONNECTED = "wifi_disconnected"  # WiFi disconnected, BLE re-enabled


class NetworkStateMachine:
    """State machine for managing network service transitions."""
    
    def __init__(self,
                 network_monitor: NetworkMonitor,
                 ble_manager: BLEServiceManager,
                 config: Optional[Dict[str, Any]] = None):
        """
        Initialize network state machine.
        
        Args:
            network_monitor: Network monitor instance
            ble_manager: BLE service manager instance
            config: Configuration dictionary
        """
        self.network_monitor = network_monitor
        self.ble_manager = ble_manager
        self.config = config or {}
        
        # Configuration parameters
        self.auto_disable = self.config.get('auto_disable', True)
        self.disable_delay = self.config.get('disable_delay', 10)
        self.connectivity_checks = self.config.get('connectivity_checks', 3)
        self.monitor_interval = self.config.get('monitor_interval', 5)
        self.reconnect_timeout = self.config.get('reconnect_timeout', 30)  # Story 4: reconnect timeout
        self.wifi_timeout = self.config.get('wifi_timeout', 30)  # Disconnect timeout
        
        # State management
        self._current_state = NetworkState.BLE_ACTIVE
        self._previous_state = None
        self._state_callbacks: Dict[NetworkState, list] = {}
        self._transition_callbacks: list = []
        
        # Timing
        self._wifi_disconnect_time: Optional[datetime] = None
        self._state_enter_time = datetime.now()
        
        # Tasks
        self._state_task: Optional[asyncio.Task] = None
        self._running = False
        
    async def start(self):
        """Start the state machine."""
        if self._running:
            logger.warning("State machine already running")
            return
            
        self._running = True
        
        # Register network monitor callback
        self.network_monitor.register_status_callback(self._on_network_status_change)
        
        # Start network monitoring
        await self.network_monitor.start_monitoring()
        
        # Start BLE service initially
        await self.ble_manager.start_service()
        
        # Start state management task
        self._state_task = asyncio.create_task(self._state_management_loop())
        
        logger.info(f"State machine started in state: {self._current_state.value}")
        
    async def stop(self):
        """Stop the state machine."""
        if not self._running:
            return
            
        self._running = False
        
        # Stop state management task
        if self._state_task:
            self._state_task.cancel()
            try:
                await self._state_task
            except asyncio.CancelledError:
                pass
                
        # Stop network monitoring
        await self.network_monitor.stop_monitoring()
        
        # Unregister callback
        self.network_monitor.unregister_status_callback(self._on_network_status_change)
        
        logger.info("State machine stopped")
        
    async def _state_management_loop(self):
        """Main state management loop."""
        while self._running:
            try:
                await self._handle_current_state()
                await asyncio.sleep(1)  # Check state every second
            except Exception as e:
                logger.error(f"Error in state management loop: {e}")
                await asyncio.sleep(5)
                
    async def _handle_current_state(self):
        """Handle logic for current state."""
        
        if self._current_state == NetworkState.BLE_ACTIVE:
            # BLE is active, waiting for WiFi configuration
            # Check if WiFi becomes connected
            if await self.network_monitor.check_wifi_status():
                await self._transition_to(NetworkState.WIFI_CONNECTING)
                
        elif self._current_state == NetworkState.WIFI_CONNECTING:
            # WiFi is connecting, wait for stable connection
            if await self.network_monitor.check_wifi_status():
                # Start verification process
                await self._transition_to(NetworkState.WIFI_VERIFYING)
            else:
                # Connection failed, go back to BLE active
                await self._transition_to(NetworkState.BLE_ACTIVE)
                
        elif self._current_state == NetworkState.WIFI_VERIFYING:
            # Verify network connectivity
            if await self.network_monitor.verify_connectivity(
                num_checks=self.connectivity_checks,
                check_interval=10
            ):
                # Connectivity verified, disable BLE
                if self.auto_disable:
                    await asyncio.sleep(self.disable_delay)
                    await self._transition_to(NetworkState.WIFI_CONNECTED)
            else:
                # Verification failed, go back to BLE active
                await self._transition_to(NetworkState.BLE_ACTIVE)
                
        elif self._current_state == NetworkState.WIFI_CONNECTED:
            # WiFi connected, BLE disabled
            # Monitor for disconnection
            if not await self.network_monitor.check_wifi_status():
                self._wifi_disconnect_time = datetime.now()
                await self._transition_to(NetworkState.WIFI_DISCONNECTED)
                
        elif self._current_state == NetworkState.WIFI_DISCONNECTED:
            # WiFi disconnected, check if we should re-enable BLE
            if self._wifi_disconnect_time:
                elapsed = datetime.now() - self._wifi_disconnect_time
                if elapsed > timedelta(seconds=self.wifi_timeout):
                    # Timeout exceeded, re-enable BLE
                    await self._transition_to(NetworkState.BLE_ACTIVE)
                elif await self.network_monitor.check_wifi_status():
                    # WiFi reconnected, go back to verifying
                    self._wifi_disconnect_time = None
                    await self._transition_to(NetworkState.WIFI_VERIFYING)
                    
    async def _transition_to(self, new_state: NetworkState):
        """
        Transition to a new state.
        
        Args:
            new_state: Target state
        """
        if self._current_state == new_state:
            return
            
        old_state = self._current_state
        logger.info(f"State transition: {old_state.value} -> {new_state.value}")
        
        # Execute exit actions for current state
        await self._execute_exit_actions(old_state)
        
        # Update state
        self._previous_state = old_state
        self._current_state = new_state
        self._state_enter_time = datetime.now()
        
        # Execute entry actions for new state
        await self._execute_entry_actions(new_state)
        
        # Notify transition callbacks
        for callback in self._transition_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(old_state, new_state)
                else:
                    callback(old_state, new_state)
            except Exception as e:
                logger.error(f"Error in transition callback: {e}")
                
    async def _execute_entry_actions(self, state: NetworkState):
        """Execute actions when entering a state."""
        logger.debug(f"Executing entry actions for {state.value}")
        
        if state == NetworkState.BLE_ACTIVE:
            # Start BLE service
            await self.ble_manager.start_service()
            
        elif state == NetworkState.WIFI_CONNECTED:
            # Stop BLE service
            await self.ble_manager.stop_service()
            
        # Execute registered callbacks
        if state in self._state_callbacks:
            for callback in self._state_callbacks[state]:
                try:
                    if asyncio.iscoroutinefunction(callback):
                        await callback()
                    else:
                        callback()
                except Exception as e:
                    logger.error(f"Error in state entry callback: {e}")
                    
    async def _execute_exit_actions(self, state: NetworkState):
        """Execute actions when exiting a state."""
        logger.debug(f"Executing exit actions for {state.value}")
        # Add any state-specific exit actions here
        
    def _on_network_status_change(self, status: ConnectivityStatus):
        """
        Handle network status change from monitor.
        
        Args:
            status: New connectivity status
        """
        logger.info(f"Network status changed to: {status.value}")
        
        # Handle forced transitions based on connectivity status
        if status == ConnectivityStatus.DISCONNECTED:
            if self._current_state == NetworkState.WIFI_CONNECTED:
                self._wifi_disconnect_time = datetime.now()
                asyncio.create_task(self._transition_to(NetworkState.WIFI_DISCONNECTED))
                
    def register_state_callback(self, state: NetworkState, callback: Callable):
        """
        Register callback for state entry.
        
        Args:
            state: State to register callback for
            callback: Function to call on state entry
        """
        if state not in self._state_callbacks:
            self._state_callbacks[state] = []
        self._state_callbacks[state].append(callback)
        
    def register_transition_callback(self, callback: Callable):
        """
        Register callback for state transitions.
        
        Args:
            callback: Function to call on state transition
        """
        self._transition_callbacks.append(callback)
        
    def get_current_state(self) -> NetworkState:
        """Get current state."""
        return self._current_state
        
    def get_state_info(self) -> Dict[str, Any]:
        """Get detailed state information."""
        return {
            "current_state": self._current_state.value,
            "previous_state": self._previous_state.value if self._previous_state else None,
            "state_duration": (datetime.now() - self._state_enter_time).total_seconds(),
            "auto_disable": self.auto_disable,
            "ble_status": self.ble_manager.get_service_status().value,
            "network_status": self.network_monitor.get_status().value,
            "force_mode": self.ble_manager.get_force_mode()
        }
        
    async def force_ble_mode(self, mode: Optional[str]):
        """
        Force BLE service mode.
        
        Args:
            mode: "enabled", "disabled", or None for auto
        """
        self.ble_manager.set_force_mode(mode)
        
        if mode == "enabled":
            # Force enable BLE
            await self.ble_manager.start_service()
        elif mode == "disabled":
            # Force disable BLE
            await self.ble_manager.stop_service()
        else:
            # Auto mode - re-evaluate current state
            await self._handle_current_state()